/*  
 *  Copyright 2006 Paul Jack.
 *
 *  This file is part of the Dex compiler suite. 
 *  
 *  Dex is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 2 of the License, or (at your
 *  option) any later version.
 *  
 *  Dex is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 *  Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */
package dex.compiler.model.expression;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;


/**
 * Infix operators.
 */
public enum Infix {
	
	
	/** The <code>||</code> operator. */
	LOGICAL_OR("||", 0, Infix.BOOLEAN),
	
	/** The <code>&&</code> operator. */
	LOGICAL_AND("&&", 1, Infix.BOOLEAN),

	/** The <code>|</code> operator. */
	OR("|", 2, Infix.INTEGER),
	
	/** The <code>^</code> operator. */
	XOR("^", 2, Infix.INTEGER),

	/** The <code>|</code> operator. */
	AND("&", 3, Infix.INTEGER),

	/** The <code>==</code> operator. */
	EQUAL("==", 4, Infix.INTEGER | Infix.FLOAT | Infix.BOOLEAN | Infix.COMPOUND | Infix.COMPARISON),

	/** The <code>!=</code> operator. */
	NOT_EQUAL("!=", 4, Infix.INTEGER | Infix.FLOAT | Infix.BOOLEAN | Infix.COMPOUND | Infix.COMPARISON),
	
	/** The <code>&gt;</code> operator. */
	GREATER(">", 5, Infix.INTEGER | Infix.FLOAT | Infix.COMPARISON),

	/** The <code>&lt;</code> operator. */
	LESS("<", 5, Infix.INTEGER | Infix.FLOAT | Infix.COMPARISON),

	/** The <code>&gt;=</code> operator. */
	GREATER_OR_EQUAL(">=", 5, Infix.INTEGER | Infix.FLOAT | Infix.COMPARISON),

	/** The <code>&lt;=</code> operator. */
	LESS_OR_EQUAL("<=", 5, Infix.INTEGER | Infix.FLOAT | Infix.COMPARISON),
	
	/** The <code>&lt;&lt;</code> operator. */
	SHIFT_LEFT("<<", 6, Infix.INTEGER),

	/** The <code>&gt;&gt;</code> operator. */
	SHIFT_RIGHT(">>", 6, Infix.INTEGER),

	/** The <code>+</code> operator. */
	ADD("+", 7, Infix.INTEGER | Infix.FLOAT),
	
	/** The <code>-</code> operator. */
	SUBTRACT("-", 7, Infix.INTEGER | Infix.FLOAT),
	
	/** The <code>*</code> operator. */
	MULTIPLY("*", 8, Infix.INTEGER | Infix.FLOAT),
	
	/** The <code>/</code> operator. */
	DIVIDE("/", 8, Infix.INTEGER | Infix.FLOAT),
	
	/** The <code>%</code> operator. */
	MODULO("%", 8, Infix.INTEGER);


	/** 
	 * The maximum priority of an operator. 
	 */
	final public static int MAX_PRIORITY = 8;

	
	/**
	 * The set of operator symbols.  Helps the parser along.
	 * This set is read-only.
	 */
	final public static Set<String> SYMBOLS = makeSymbolSet();
	
	
	/** Bit that indicates that the operator works on integer types. */
	final private static int INTEGER = 1;
	
	/** Bit that indicates that the operator works on floating-point types. */
	final private static int FLOAT = 2;
	
	/** Bit that indicates that the operator works on boolean types. */
	final private static int BOOLEAN = 4;
	
	/** Bit that indicates that the operator works on compound types. */
	final private static int COMPOUND = 8;
	
	/** Bit that indicates that the operator is a comparison operator. */
	final private static int COMPARISON = 16;


	/**
	 * The priority of this operator.
	 */
	private int priority;
	
	
	/**
	 * The symbol of this operator.
	 */
	private String symbol;
	
	
	/**
	 * The flags for this operator.  Bits are defined by the private static
	 * int fields above.
	 */
	private int flags;
	
	
	/**
	 * Constructs a new operator
	 * 
	 * @param symbol     the symbol
	 * @param priority  the priority
	 * @param flags      the bit flags 
	 */
	private Infix(String symbol, int priority, int flags) {
		this.symbol = symbol;
		this.priority = priority;
		this.flags = flags;
	}
	
	
	/**
	 * Returns the priority of the operator.
	 * 
	 * @return  the priority of the operator.
	 */
	public int getPriority() {
		return priority;
	}
	
	
	/**
	 * Returns the symbol for the operator.
	 *
	 * @return  the symbol for the operator
	 */
	public String getSymbol() {
		return symbol;
	}
	
	
	/**
	 * Returns the operator for a given symbol.
	 * Or, null if the given string is not an operator symbol.
	 * 
	 * @param symbol  the symbol whose operator to return 
	 * @return  that operator, or null if not found
	 */
	public static Infix get(String symbol) {
		for (Infix op : values()) {
			if (op.symbol.equals(symbol)) {
				return op;
			}
		}
		return null;
	}

	
	/**
	 * Makes the set of symbols.
	 *
	 * @return  the set of all infix operator symbols
	 */
	private static Set<String> makeSymbolSet() {
		HashSet<String> result = new HashSet<String>();
		for (Infix op : values()) {
			result.add(op.getSymbol());
		}
		return Collections.unmodifiableSet(result);
	}
	
	
	/**
	 * Returns true if an operator can operate on integer types.
	 * 
	 * @return  true if an operator can operate on integer types
	 */
	public boolean isInteger() {
		return (flags & INTEGER) == INTEGER;
	}
	
	
	/**
	 * Returns true if an operator can operate on compound types.
	 * Only the <code>==</code> and <code>!=</code> operators
	 * will return true for this.
	 * 
	 * @return  true if an operator can operate on compound types
	 */
	public boolean isCompound() {
		return (flags & COMPOUND) == COMPOUND;
	}
	
	
	/**
	 * Returns true if an operator can operate on the boolean type.
	 * 
	 * @return  true if an operator can operate on the boolean type
	 */
	public boolean isBoolean() {
		return (flags & BOOLEAN) == BOOLEAN;
	}
	
	
	/**
	 * Returns true if an operator can operate on floating-point types
	 * 
	 * @return  true if an operator can operate on floating-point types
	 */
	public boolean isFloat() {
		return (flags & FLOAT) == FLOAT;
	}

	
	/**
	 * Returns true if an operator is a comparison operator.
	 * Comparison operators evaluate to a boolean result; non-comparison
	 * operators evaluate to the same type as the operands.
	 * 
	 * @return  true if an operator is a comparison operator
	 */
	public boolean isComparison() {
		return (flags & COMPARISON) == COMPARISON;
	}

}
